﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Management;
using Microsoft.Win32;

namespace PowerTriggersWcf
{
    [DataContract(Namespace = "http://PWTWcf")]
    internal class TaskDevice : Task
    {
        private static IDictionary<string, string> _categoryNames = new Dictionary<string, string>();

        #region constructors
        private TaskDevice()
            : base(TaskTypes.Device, true)
        {
        }

        internal TaskDevice(bool setDefaultProperties)
            : base(TaskTypes.Device, setDefaultProperties)
        {
        }

        internal TaskDevice(ManagementObject device, int sequence)
            : this()
        {
            this.Description = (string)device.GetPropertyValue("Name");
            this.Sequence = sequence;
            this.Status = (string)device.GetPropertyValue("Status");
            this.ID = (string)device.GetPropertyValue("DeviceID");
            this.Category = GetCategoryFromClassGuid((string)device.GetPropertyValue("ClassGuid"));

            SetNonContractProperties(device);
        }

        private void SetNonContractProperties(ManagementObject device)
        {
            this.ClassGuid = new Guid((string)device.GetPropertyValue("ClassGuid"));
            this.Manufacturer = (string)device.GetPropertyValue("Manufacturer");
            this.SystemCreationClassName = (string)device.GetPropertyValue("SystemCreationClassName");
            this.PNPDeviceID = (string)device.GetPropertyValue("PNPDeviceID");
        }

        private string GetCategoryFromClassGuid(string classGuid)
        {
            try
            {
                if (!_categoryNames.ContainsKey(classGuid))
                {
                    var key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\" + classGuid, RegistryKeyPermissionCheck.Default, System.Security.AccessControl.RegistryRights.QueryValues);
                    if (key != null)
                    {
                        var value = key.GetValue(string.Empty); // get the default value
                        if (value != null)
                        {
                            value = (string)value;
                        }
                        else
                        {
                            value = classGuid;
                        }
                        key.Close();
                        _categoryNames[classGuid] = (string)value;
                    }
                    else
                    {
                        _categoryNames[classGuid] = classGuid;
                    }
                }

                return _categoryNames[classGuid];
            }
            catch { }

            return classGuid;
        }

        internal Guid ClassGuid { get; private set; }

        internal string Manufacturer { get; private set; }

        internal string SystemCreationClassName { get; private set; }

        internal string PNPDeviceID { get; private set; }

        #endregion

        internal override bool PerformAction(HistoryHeader historyHeader)
        {
            bool success;

            try
            {
                using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * from Win32_PnpEntity WHERE DeviceID = '" + this.ID.Replace(@"\", @"\\") + "'"))
                {
                    using (ManagementObject device = searcher.Get().OfType<ManagementObject>().First())
                    {
                        SetNonContractProperties(device);
                    }
                }

                TaskPropertyChoice action = this.GetSelectedAction();

                switch (action.ChoiceType)
                {
                    case TaskPropertyChoiceTypes.Enable:
                        {
                            SetupApiError err = DeviceHelper.SetDeviceEnabled(this.ClassGuid, this.ID, true);
                            if (err == SetupApiError.Success)
                            {
                                Service.AddHistory(historyHeader, HistorySeverityLevels.Success, string.Format("Device '{0}' successfully enabled", this.Description), false);
                            }
                            else
                            {
                                Service.AddHistory(historyHeader, HistorySeverityLevels.Error, string.Format("Unable to enable device '{0}'. The reported error code is '{1}'.", this.Description, err.ToString()), false);
                            }
                            success = (err == SetupApiError.Success);
                        }
                        break;
                    case TaskPropertyChoiceTypes.Disable:
                        {
                            SetupApiError err = DeviceHelper.SetDeviceEnabled(this.ClassGuid, this.ID, false);
                            if (err == SetupApiError.Success)
                            {
                                Service.AddHistory(historyHeader, HistorySeverityLevels.Success, string.Format("Device '{0}' successfully disabled", this.Description), false);
                            }
                            else
                            {
                                Service.AddHistory(historyHeader, HistorySeverityLevels.Error, string.Format("Unable to disable device '{0}'. The reported error code is '{1}'.", this.Description, err.ToString()), false);
                            }
                            success = (err == SetupApiError.Success);
                        }
                        break;
                    case TaskPropertyChoiceTypes.ReEnable:
                        {
                            SetupApiError err = DeviceHelper.SetDeviceEnabled(this.ClassGuid, this.ID, false);
                            if (err == SetupApiError.Success)
                            {
                                err = DeviceHelper.SetDeviceEnabled(this.ClassGuid, this.ID, true);
                                if (err == SetupApiError.Success)
                                {
                                    Service.AddHistory(historyHeader, HistorySeverityLevels.Success, string.Format("Device '{0}' successfully re-enabled", this.Description), false);
                                }
                                else
                                {
                                    Service.AddHistory(historyHeader, HistorySeverityLevels.Error, string.Format("Unable to enable re-enabled '{0}'. The reported error code is '{1}'.", this.Description, err.ToString()), false);
                                }
                            }
                            else
                            {
                                Service.AddHistory(historyHeader, HistorySeverityLevels.Error, string.Format("Unable to re-enabled device '{0}'. The reported error code is '{1}'.", this.Description, err.ToString()), false);
                            }
                            success = (err == SetupApiError.Success);
                        }
                        break;
                    default:
                        throw new NotSupportedException(string.Format("DeviceAction '{0}' is not a supported device action for device '{1}'", action.ChoiceType.ToString(), this.Description));
                }
            }
            catch (System.Exception ex)
            {
                string msg = string.Format("Error when attempting '{0}' on device '{1}'. Error={2}", this.GetSelectedAction().ChoiceType.ToString(), this.Description, ex.Message.Trim());
                Service.AddHistory(historyHeader, HistorySeverityLevels.Error, msg, false);
                success = false;
            }

            return success;
        }
    }
}
